/* ----------------------------------------------------------------------------
 * Copyright (c) Huawei Technologies Co., Ltd. 2013-2019. All rights reserved.
 * Description: Mutex
 * Author: huangjieliang
 * Create: 2013-01-01
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 * 1. Redistributions of source code must retain the above copyright notice, this list of
 * conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright notice, this list
 * of conditions and the following disclaimer in the documentation and/or other materials
 * provided with the distribution.
 * 3. Neither the name of the copyright holder nor the names of its contributors may be used
 * to endorse or promote products derived from this software without specific prior written
 * permission.
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 * --------------------------------------------------------------------------- */
/* ----------------------------------------------------------------------------
 * Notice of Export Control Law
 * ===============================================
 * Huawei LiteOS may be subject to applicable export control laws and regulations, which might
 * include those applicable to Huawei LiteOS of U.S. and the country in which you are located.
 * Import, export and usage of Huawei LiteOS in any manner by you shall be in compliance with such
 * applicable export control laws and regulations.
 * --------------------------------------------------------------------------- */

#include "los_mux_pri.h"
#include "los_mux_debug_pri.h"
#include "los_err_pri.h"
#include "los_memory_pri.h"
#include "los_priqueue_pri.h"
#include "los_bitmap.h"
#include "los_exc.h"
#include "los_mux_debug_pri.h"

#if (LOSCFG_BASE_CORE_SWTMR == YES)
#include "los_swtmr_pri.h"
#endif

#ifdef __cplusplus
#if __cplusplus
extern "C" {
#endif
#endif /* __cplusplus */

#if (LOSCFG_BASE_IPC_MUX == YES)

LITE_OS_SEC_BSS LosMuxCB *g_allMux;

LITE_OS_SEC_DATA_INIT LOS_DL_LIST g_unusedMuxList;

/*****************************************************************************
 Funtion	     : OsMuxInit,
 Description  : Initializes the mutex,
 Input        : None
 Output       : None
 Return       : LOS_OK on success ,or error code on failure
 *****************************************************************************/
LITE_OS_SEC_TEXT_INIT UINT32 OsMuxInit(VOID)
{
    LosMuxCB *muxNode = NULL;
    UINT32 index;

    LOS_ListInit(&g_unusedMuxList);
    if (LOSCFG_BASE_IPC_MUX_LIMIT <= 0) { /*lint !e506*/
        return LOS_ERRNO_MUX_MAXNUM_ZERO;
    }

    g_allMux = (LosMuxCB *)LOS_MemAlloc(m_aucSysMem0, (LOSCFG_BASE_IPC_MUX_LIMIT * sizeof(LosMuxCB)));
    if (g_allMux == NULL) {
        return LOS_ERRNO_MUX_MAXNUM_ZERO;
    }

    for (index = 0; index < LOSCFG_BASE_IPC_MUX_LIMIT; index++) {
        muxNode = ((LosMuxCB *)g_allMux) + index;
        muxNode->muxID = index;
        muxNode->muxStat = OS_MUX_UNUSED;
        LOS_ListTailInsert(&g_unusedMuxList, &muxNode->muxList);
    }

    osMuxDbgInitHook();

    return LOS_OK;
}

/*****************************************************************************
 Function     : LOS_MuxCreate
 Description  : Create a mutex,
 Input        : None
 Output       : muxHandle ------ Mutex operation handle
 Return       : LOS_OK on success ,or error code on failure
 *****************************************************************************/
LITE_OS_SEC_TEXT_INIT UINT32 LOS_MuxCreate(UINT32 *muxHandle)
{
    UINT32 intSave;
    LosMuxCB *muxCreated = NULL;
    LOS_DL_LIST *unusedMux = NULL;
    UINT32 errNo;
    UINT32 errLine;

    if (muxHandle == NULL) {
        return LOS_ERRNO_MUX_PTR_NULL;
    }

    intSave = LOS_IntLock();
    if (LOS_ListEmpty(&g_unusedMuxList)) {
        LOS_IntRestore(intSave);
        osMutexCheckHook();
        OS_GOTO_ERR_HANDLER(LOS_ERRNO_MUX_ALL_BUSY);
    }

    unusedMux = LOS_DL_LIST_FIRST(&(g_unusedMuxList));
    LOS_ListDelete(unusedMux);
    muxCreated = LOS_DL_LIST_ENTRY(unusedMux, LosMuxCB, muxList); /*lint !e413*/
    muxCreated->muxCount = 0;
    muxCreated->muxStat = OS_MUX_USED;
    muxCreated->owner = (LosTaskCB *)NULL;
    LOS_ListInit(&muxCreated->muxList);
    *muxHandle = (UINT32)muxCreated->muxID;

    osMuxDbgUpdateHook(muxCreated->muxID, (VOID *)g_losTask.runTask->taskEntry); /*lint !e611*/

    LOS_IntRestore(intSave);
    return LOS_OK;
ErrHandler:
    OS_RETURN_ERROR_P2(errLine, errNo);
}

/*****************************************************************************
 Function     : LOS_MuxDelete
 Description  : Delete a mutex,
 Input        : muxHandle------Mutex operation handle
 Output       : None
 Return       : LOS_OK on success ,or error code on failure
 *****************************************************************************/
LITE_OS_SEC_TEXT_INIT UINT32 LOS_MuxDelete(UINT32 muxHandle)
{
    UINT32 intSave;
    LosMuxCB *muxDeleted = NULL;
    UINT32 errNo;
    UINT32 errLine;

    if (GET_MUXINDEX(muxHandle) >= (UINT32)LOSCFG_BASE_IPC_MUX_LIMIT) {
        OS_GOTO_ERR_HANDLER(LOS_ERRNO_MUX_INVALID);
    }

    muxDeleted = GET_MUX(muxHandle);
    intSave = LOS_IntLock();
    if (muxDeleted->muxID != muxHandle || muxDeleted->muxStat == OS_MUX_UNUSED) {
        LOS_IntRestore(intSave);
        OS_GOTO_ERR_HANDLER(LOS_ERRNO_MUX_INVALID);
    }

    if (!LOS_ListEmpty(&muxDeleted->muxList) || muxDeleted->muxCount) {
        LOS_IntRestore(intSave);
        OS_GOTO_ERR_HANDLER(LOS_ERRNO_MUX_PENDED);
    }

    LOS_ListTailInsert(&g_unusedMuxList, &muxDeleted->muxList);
    muxDeleted->muxStat = OS_MUX_UNUSED;
    muxDeleted->muxID = SET_MUXID((GET_MUXCOUNT(muxDeleted->muxID) + 1), GET_MUXINDEX(muxDeleted->muxID));

    osMuxDbgUpdateHook(muxDeleted->muxID, NULL); /*lint !e611*/

    LOS_IntRestore(intSave);

    return LOS_OK;
ErrHandler:
    OS_RETURN_ERROR_P2(errLine, errNo);
}

/*****************************************************************************
 Function     : LOS_MuxPend
 Description  : Specify the mutex P operation,
 Input        : muxHandle ------ Mutex operation handleone,
                timeout  ------- waiting time,
 Output       : None
 Return       : LOS_OK on success ,or error code on failure
 *****************************************************************************/
LITE_OS_SEC_TEXT UINT32 LOS_MuxPend(UINT32 muxHandle, UINT32 timeout)
{
    UINT32 intSave;
    LosMuxCB *muxPended = NULL;
    UINT32 retErr;
    LosTaskCB *runTsk = NULL;
    LosTaskCB *pendedTask1 = NULL;
    LosTaskCB *pendedTask2 = NULL;
    LOS_DL_LIST *pendObj = NULL;
    UINT16 bitMapPri;

    if (GET_MUXINDEX(muxHandle) >= (UINT32)LOSCFG_BASE_IPC_MUX_LIMIT) {
        OS_RETURN_ERROR(LOS_ERRNO_MUX_INVALID);
    }

    muxPended = GET_MUX(muxHandle);
    intSave = LOS_IntLock();
    if ((muxPended->muxStat == OS_MUX_UNUSED) || (muxPended->muxID != muxHandle)) {
        LOS_IntRestore(intSave);
        OS_RETURN_ERROR(LOS_ERRNO_MUX_INVALID);
    }

    osMuxDbgTimeUpdateHook(muxPended->muxID);

    if (OS_INT_ACTIVE) {
        LOS_IntRestore(intSave);
        return LOS_ERRNO_MUX_PEND_INTERR;
    }

#if (LOSCFG_BASE_CORE_SWTMR == YES)
    /* DO NOT Call blocking API in software timer task */
    if (g_losTask.runTask->taskEntry == (TSK_ENTRY_FUNC)OsSwtmrTask) {
        PRINTK("Warning: DO NOT call %s in software timer callback.\n", __FUNCTION__);
        osBackTrace();
    }
#endif

    runTsk = (LosTaskCB *)g_losTask.runTask;
    if (muxPended->muxCount == 0) {
        osMuxDlockNodeInsertHook(runTsk->taskID, muxPended);
        muxPended->muxCount++;
        muxPended->owner = runTsk;
        LOS_IntRestore(intSave);
        return LOS_OK;
    }

    if (muxPended->owner == runTsk) {
        muxPended->muxCount++;
        LOS_IntRestore(intSave);
        return LOS_OK;
    }

    if (!timeout) {
        LOS_IntRestore(intSave);
        return LOS_ERRNO_MUX_UNAVAILABLE;
    }

    if (g_losTaskLock) {
        retErr = LOS_ERRNO_MUX_PEND_IN_LOCK;
        PRINT_ERR("!!!LOS_ERRNO_MUX_PEND_IN_LOCK!!!\n");
        osBackTrace();
        goto errre_uniMuxPend;
    }

    OsPriqueueDequeue(&runTsk->pendList);
    runTsk->taskStatus &= (~OS_TASK_STATUS_READY);
    runTsk->taskMux = (VOID *)muxPended;
    pendObj = &runTsk->pendList;
    runTsk->taskStatus |= OS_TASK_STATUS_PEND;

    if (muxPended->owner->priority > runTsk->priority) {
        LOS_BitMapSet(&(muxPended->owner->priBitMap), muxPended->owner->priority);
        OsTaskPriModify(muxPended->owner, runTsk->priority);
    }

    if (LOS_ListEmpty(&muxPended->muxList)) {
        LOS_ListTailInsert(&muxPended->muxList, pendObj);
    } else {
        pendedTask1 = OS_TCB_FROM_PENDLIST(LOS_DL_LIST_FIRST(&muxPended->muxList));
        pendedTask2 = OS_TCB_FROM_PENDLIST(LOS_DL_LIST_LAST(&muxPended->muxList));
        if (pendedTask1 != NULL && pendedTask1->priority > runTsk->priority) {
            LOS_ListHeadInsert(&muxPended->muxList, pendObj);
        } else if ((pendedTask2 != NULL) && pendedTask2->priority <= runTsk->priority) {
            LOS_ListTailInsert(&muxPended->muxList, pendObj);
        } else {
            LOS_DL_LIST_FOR_EACH_ENTRY(pendedTask1, (&(muxPended->muxList)), LosTaskCB, pendList) /*lint !e413*/
            {
                if (pendedTask1->priority < runTsk->priority) {
                    continue;
                } else if (pendedTask1->priority > runTsk->priority) {
                    LOS_ListAdd(pendedTask1->pendList.pstPrev, pendObj);
                    break;
                } else {
                    LOS_ListAdd(&pendedTask1->pendList, pendObj);
                    break;
                }
            }
        }
    }

    if (timeout != LOS_WAIT_FOREVER) {
        runTsk->taskStatus |= OS_TASK_STATUS_PEND_TIME;
        OsTaskAdd2TimerList((LosTaskCB *)runTsk, timeout);
        (VOID)LOS_IntRestore(intSave);
        LOS_Schedule();
        intSave = LOS_IntLock();
        if (muxPended->owner->priority >= runTsk->priority) {
            bitMapPri = LOS_LowBitGet(muxPended->owner->priBitMap);
            if (bitMapPri != LOS_INVALID_BIT_INDEX) {
                LOS_BitMapClr(&(muxPended->owner->priBitMap), bitMapPri);
                OsTaskPriModify(muxPended->owner, bitMapPri);
            }
        } else {
            if (LOS_HighBitGet(muxPended->owner->priBitMap) != runTsk->priority) {
                LOS_BitMapClr(&(muxPended->owner->priBitMap), runTsk->priority);
            }
        }

        (VOID)LOS_IntRestore(intSave);
    } else {
        (VOID)LOS_IntRestore(intSave);
        LOS_Schedule();
    }

    if (runTsk->taskStatus & OS_TASK_STATUS_TIMEOUT) {
        intSave = LOS_IntLock();
        runTsk->taskStatus &= (~OS_TASK_STATUS_TIMEOUT);
        (VOID)LOS_IntRestore(intSave);
        retErr = LOS_ERRNO_MUX_TIMEOUT;
        goto error_uniMuxPend;
    }
    return LOS_OK;

errre_uniMuxPend:
    (VOID)LOS_IntRestore(intSave);
error_uniMuxPend:
    OS_RETURN_ERROR(retErr);
}

/*****************************************************************************
 Function     : LOS_MuxPost
 Description  : Specify the mutex V operation,
 Input        : muxHandle ------ Mutex operation handle,
 Output       : None
 Return       : LOS_OK on success ,or error code on failure
 *****************************************************************************/
LITE_OS_SEC_TEXT UINT32 LOS_MuxPost(UINT32 muxHandle)
{
    UINT32 intSave;
    LosMuxCB *muxPosted = GET_MUX(muxHandle);
    LosTaskCB *resumedTask = NULL;
    LosTaskCB *pendedTask = NULL;
    LosTaskCB *runTsk = NULL;
    UINT16 bitMapPri;

    intSave = LOS_IntLock();

    if ((GET_MUXINDEX(muxHandle) >= (UINT32)LOSCFG_BASE_IPC_MUX_LIMIT) ||
        (muxPosted->muxStat == OS_MUX_UNUSED) || muxPosted->muxID != muxHandle) {
        LOS_IntRestore(intSave);
        OS_RETURN_ERROR(LOS_ERRNO_MUX_INVALID);
    }

    osMuxDbgTimeUpdateHook(muxPosted->muxID);

    if (OS_INT_ACTIVE) {
        LOS_IntRestore(intSave);
        OS_RETURN_ERROR(LOS_ERRNO_MUX_PEND_INTERR);
    }

    runTsk = (LosTaskCB *)g_losTask.runTask;
    if (muxPosted->muxCount == 0 || muxPosted->owner != runTsk) {
        LOS_IntRestore(intSave);
        OS_RETURN_ERROR(LOS_ERRNO_MUX_INVALID);
    }

    if (--(muxPosted->muxCount) != 0) {
        LOS_IntRestore(intSave);
        return LOS_OK;
    }

    if (!LOS_ListEmpty(&muxPosted->muxList)) {
        resumedTask = OS_TCB_FROM_PENDLIST(LOS_DL_LIST_FIRST(&(muxPosted->muxList))); /*lint !e413*/
        LOS_ListDelete(LOS_DL_LIST_FIRST(&(muxPosted->muxList)));
        LOS_ASSERT_COND(resumedTask->taskStatus & OS_TASK_STATUS_PEND);
        resumedTask->taskStatus &= (~OS_TASK_STATUS_PEND);
        if (resumedTask->taskStatus & OS_TASK_STATUS_PEND_TIME) {
            OsTimerListDelete(resumedTask);
            resumedTask->taskStatus &= (~OS_TASK_STATUS_PEND_TIME);
        }

        if (resumedTask->priority > runTsk->priority) {
            if (LOS_HighBitGet(runTsk->priBitMap) != resumedTask->priority) {
                LOS_BitMapClr(&runTsk->priBitMap, resumedTask->priority);
            }
        } else {
            if (runTsk->priBitMap != 0) {
                if (!LOS_ListEmpty(&muxPosted->muxList)) {
                    bitMapPri = LOS_HighBitGet(runTsk->priBitMap);
                    LOS_DL_LIST_FOR_EACH_ENTRY(pendedTask, (&(muxPosted->muxList)), LosTaskCB, pendList) /*lint !e413*/
                    {
                        if (bitMapPri != pendedTask->priority) {
                            LOS_BitMapClr(&runTsk->priBitMap, pendedTask->priority);
                        }
                    }
                }

                bitMapPri = LOS_LowBitGet(runTsk->priBitMap);
                LOS_BitMapClr(&runTsk->priBitMap, bitMapPri);
                OsTaskPriModify(muxPosted->owner, bitMapPri);
            }
        }

        muxPosted->muxCount = 1;
        muxPosted->owner = resumedTask;
        resumedTask->taskMux = NULL;
        osMuxDlockNodeDeleteHook(runTsk->taskID, muxPosted);
        osMuxDlockNodeInsertHook(resumedTask->taskID, muxPosted);

        if (!(resumedTask->taskStatus & OS_TASK_STATUS_SUSPEND)) {
            resumedTask->taskStatus |= OS_TASK_STATUS_READY;
            OsPriqueueEnqueue(&resumedTask->pendList, resumedTask->priority);
        }
        (VOID)LOS_IntRestore(intSave);
        LOS_Schedule();
    } else {
        muxPosted->owner = (LosTaskCB *)NULL;
        osMuxDlockNodeDeleteHook(runTsk->taskID, muxPosted);
        (VOID)LOS_IntRestore(intSave);
    }
    return LOS_OK;
}

#endif /* (LOSCFG_BASE_IPC_MUX == YES) */

#ifdef __cplusplus
#if __cplusplus
}
#endif
#endif /* __cplusplus */

